<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html><head><title>Programming in Lua : 28.2</title>
<link rel="stylesheet" type="text/css" href="pil.css">
</head>
<body>
<p class="warning">
<A HREF="contents.html"><IMG TITLE="Programming in Lua (first edition)" SRC="capa.jpg" ALT="" ALIGN="left"></A>This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.<BR>The third edition targets Lua 5.2 and is available at <A HREF="http://www.amazon.com/exec/obidos/ASIN/859037985X/theprogrammil3-20">Amazon</A> and other bookstores.<BR>By buying the book, you also help to <A HREF="../donations.html">support the Lua project</A>.
</p>
<table width="100%" class="nav">
<tr>
<td width="10%" align="left"><a href="28.1.html"><img border="0" src="left.png" alt="Previous"></a></td>
<td width="80%" align="center"><a href="contents.html"><font face="Helvetica,Arial,sanserif">
<font color="gray">Programming in </font><font color="blue"> Lua</font>
</font></a></td>
<td width="10%" align="right"><a href="28.3.html"><img border="0" src="right.png" alt="Next"></a></td>
</tr>
<tr>
<td width="10%" align="left"></td>
<td width="80%" align="center"><a href="contents.html#P4">Part IV. The C API</a>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href="contents.html#28">Chapter 28. User-Defined Types in C</a></td>
<td width="10%" align="right"></td></tr>
</table>
<hr/>
<p><h2>28.2 &ndash; Metatables</h2>

<p>Our current implementation has a major security hole.
Suppose the user writes something like <code>array.set(io.stdin, 1, 0)</code>.
The value in <code>io.stdin</code> is a userdatum
with a pointer to a stream (<code>FILE*</code>).
Because it is a userdatum,
<code>array.set</code> will gladly accept it as a valid argument;
the probable result will be a memory corruption
(with luck you can get an index-out-of-range error instead).
Such behavior is unacceptable for any Lua library.
No matter how you use a C library,
it should not corrupt C data or produce a core dump from Lua.

<p>To distinguish arrays from other userdata,
we create a unique metatable for it.
(Remember that userdata can also have metatables.)
Then, every time we create an array,
we mark it with this metatable;
and every time we get an array,
we check whether it has the right metatable.
Because Lua code cannot change the metatable of a userdatum,
it cannot fake our code.

<p>We also need a place to store this new metatable,
so that we can access it to create new arrays and
to check whether a given userdatum is an array.
As we saw earlier,
there are two common options for storing the metatable:
in the registry,
or as an upvalue for the functions in the library.
It is customary, in Lua,
to register any new C type into the registry,
using a <em>type name</em> as the index and the metatable as the value.
As with any other registry index,
we must choose a type name with care, to avoid clashes.
We will call this new type <code>"LuaBook.array"</code>.

<p>As usual, the auxiliary library offers some functions to help us here.
The new auxiliary functions we will use are
<pre>
    int   luaL_newmetatable (lua_State *L, const char *tname);
    void  luaL_getmetatable (lua_State *L, const char *tname);
    void *luaL_checkudata (lua_State *L, int index,
                                         const char *tname);
</pre>
The <code>luaL_newmetatable</code> function
creates a new table (to be used as a metatable),
leaves the new table in the top of the stack,
and associates the table and the given name in the registry.
It does a dual association:
It uses the name as a key to the table
and the table as a key to the name.
(This dual association allows faster implementations for
the other two functions.)
The <code>luaL_getmetatable</code> function retrieves
the metatable associated with <code>tname</code> from the registry.
Finally, <code>luaL_checkudata</code> checks whether the object at the
given stack position is a userdatum with a metatable that matches
the given name.
It returns <code>NULL</code> if the object does not have the correct metatable
(or if it is not a userdata);
otherwise, it returns the userdata address.

<p>

<p>
Now we can start our implementation.
The first step it to change the function that opens the library.
The new version must create a table to be used as the metatable for arrays:
<pre>
    int luaopen_array (lua_State *L) {
      luaL_newmetatable(L, "LuaBook.array");
      luaL_openlib(L, "array", arraylib, 0);
      return 1;
    }
</pre>

<p>The next step is to change <code>newarray</code> so that it sets
this metatable in all arrays that it creates:
<pre>
    static int newarray (lua_State *L) {
      int n = luaL_checkint(L, 1);
      size_t nbytes = sizeof(NumArray) + (n - 1)*sizeof(double);
      NumArray *a = (NumArray *)lua_newuserdata(L, nbytes);
    
      luaL_getmetatable(L, "LuaBook.array");
      lua_setmetatable(L, -2);
    
      a->size = n;
      return 1;  /* new userdatum is already on the stack */
    }
</pre>
The <code>lua_setmetatable</code> function pops a table from the stack
and sets it as the metatable of the object at the given index.
In our case, this object is the new userdatum.

<p>Finally, <code>setarray</code>, <code>getarray</code>, and <code>getsize</code> have to check
whether they got a valid array as their first argument.
Because we want to raise an error in case of wrong arguments,
we define the following auxiliary function:
<pre>
    static NumArray *checkarray (lua_State *L) {
      void *ud = luaL_checkudata(L, 1, "LuaBook.array");
      luaL_argcheck(L, ud != NULL, 1, "`array' expected");
      return (NumArray *)ud;
    }
</pre>
Using <code>checkarray</code>,
the new definition for <code>getsize</code> is straightforward:
<pre>
    static int getsize (lua_State *L) {
      NumArray *a = checkarray(L);
      lua_pushnumber(L, a->size);
      return 1;
    }
</pre>

<p>Because <code>setarray</code> and <code>getarray</code> also share code to check the
index as their second argument,
we factor out their common parts in the following function:
<pre>
    static double *getelem (lua_State *L) {
      NumArray *a = checkarray(L);
      int index = luaL_checkint(L, 2);
    
      luaL_argcheck(L, 1 &lt;= index &amp;&amp; index &lt;= a->size, 2,
                       "index out of range");
    
      /* return element address */
      return &amp;a->values[index - 1];
    }
</pre>
After the definition of <code>getelem</code>,
<code>setarray</code> and <code>getarray</code> are straightforward:
<pre>
    static int setarray (lua_State *L) {
      double newvalue = luaL_checknumber(L, 3);
      *getelem(L) = newvalue;
      return 0;
    }
    
    static int getarray (lua_State *L) {
      lua_pushnumber(L, *getelem(L));
      return 1;
    }
</pre>
Now, if you try something like <code>array.get(io.stdin, 10)</code>,
you will get a proper error message:
<pre>
    error: bad argument #1 to `getarray' (`array' expected)
</pre>

<hr/>
<table width="100%" class="nav">
<tr valign="top">
<td width="90%" align="left">
<small>
  Copyright &copy; 2003&ndash;2004 Roberto Ierusalimschy.  All rights reserved.
</small>
</td>
<td width="10%" align="right"><a href="28.3.html"><img border="0" src="right.png" alt="Next"></a></td>
</tr>
</table>


</body></html>

